require "task_list/query"

# 検索用のクエリを生成するためのファクトリ
# 
class QueryFactory
  # 文字列で指定された検索クエリを解釈し、クエリを構築する。
  # @param user_id 検索対象ユーザの識別子。非null。
  # @param string 検索クエリ。非null。
  # TODO エラーチェックを行っていない。
  # TODO タスクのステータスが、DB上では「done」の2値
  # (u|user_id:user_id) (p|priority:<=priority)? (word)* (i|id:task_id)? (s|status:done:undone)? (d|date:<=2009-12-14T00:00:00)?
  def self.create(user_id, string)
    # String[]: タイトル文字列に対する検索単語列。nil=検索条件なし
    words = Array.new
    
    # Integer: タスク優先度。nil=検索条件なし
    priority = nil
    
    # String: タスク優先度の比較演算子。=|<|>|<=|>=
    priority_operator = '='
    
    # String: タスク実行状態。nil=検索条件なし、done=実行済み、undone=未実行
    status = nil
    
    # Integer: タスク識別子。nil=検索条件なし
    task_id = nil
    
    # DateTime: タスク期日。nil=検索条件なし
    date = nil
    
    # String: タスク期日の比較演算子。=|<|>|<=|>=
    priority_operator = '='
    
    # 検索クエリ文字列の解釈・分解
    user_id, task_id, priority, priority_operator, status, date, date_operator, words = parse(string);
    return create_query(user_id, task_id, priority, priority_operator, status, date, date_operator, words)
  end
  
  private
  # 与えられたクエリ文字列を解釈し、記述された各種検索条件を取得する。
  # 記述内に存在しなかった検索条件は、指定されなかったものと解釈する。
  def self.parse(string)
    words = Array.new
    user_id = nil
    priority = nil
    priority_operator = '='
    status = nil
    task_id = nil
    date = nil
    date_operator = '='
    
    terms = string.split(/\s+/);
    terms.each{|term| begin
     (key, value) = term.split(/:/, 2)
        if value == nil then
          words.push(key);
        elsif key =~ /^u|user_id$/
          user_id = value.to_i()
        elsif key =~ /^p|priority$/
          priority_string, priority_operator = divide_operator(value)
          priority = priority_string.to_i()
        elsif key =~ /^i|id$/
          task_id = value.to_i()
        elsif key =~ /^s|status$/
          status = value
        elsif key =~ /^d|date$/
          date_value, date_operator = divide_operator(value);
          date = DateTime.parse(date_value);
        else
          words.push(term)
        end
      end
    }
    return user_id, task_id, priority, priority_operator, status, date, date_operator, words
  end
  
  # 引数に指定された各種検索条件に基づき、クエリオブジェクトを生成する。
  # nil指定された引数は、検索条件未指定として扱う。
  def self.create_query(user_id, task_id, priority, priority_operator, status, date, date_operator, words)
    query = Query.new()
    query.add('user_id', user_id, '=') if(user_id != nil)
    query.add('id', task_id, '=') if (task_id != nil)
    query.add('priority', priority, priority_operator) if(priority != nil)
    query.add('status', status, '=') if (status != nil)
    query.add('date', date, date_operator) if (date != nil)
    words.each{|word|
      query.add('title', word, 'contain')
    }
    return query
  end
  
  # 与えられた文字列が、比較演算子とオペランドからなると想定し、演算子とオペランドに分解して返す。
  # @param 比較演算子とオペランドをつなげた文字列。"<=operand"など。nilならば、value=nil, operator='='を返す。比較演算子がなければ、value=string, operator='='をかえす。
  # @return value オペランド。
  # @return operator 比較演算子。
  def self.divide_operator(string)
    value = nil
    operator = nil
    
    if string =~ /^(>=|<=)/ then
      operator = string.slice!(0,2)
      value = string
    elsif string =~ /^[><=]/ then
      operator = string.slice!(0,1)
      value = string
    else
      operator = '='
      value = string
    end
    return value, operator
  end
end
